package com.aitao.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @Author : AiTao
 * @Create : 2021-11-01 23:29
 * @Description : 集合工具类
 * @see com.aitao.test.TestCollector 集合工具测试类
 */
public class Collector {
    /**
     * 集合空校验
     *
     * @param collection 集合对象
     * @param <T>        类型
     * @return {@link Boolean}
     */
    public static <T> boolean isEmpty(Collection<T> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 集合非空校验
     *
     * @param collection 集合对象
     * @param <T>        类型
     * @return {@link Boolean}
     */
    public static <T> boolean isNotEmpty(Collection<T> collection) {
        return !isEmpty(collection);
    }

    /**
     * map空校验
     *
     * @param map map对象
     * @param <K> 键类型
     * @param <V> 值类型
     * @return {@link Boolean}
     */
    public static <K, V> boolean isEmpty(Map<K, V> map) {
        return map == null || map.isEmpty();
    }

    /**
     * map非空校验
     *
     * @param map map对象
     * @param <K> 键类型
     * @param <V> 值类型
     * @return {@link Boolean}
     */
    public static <K, V> boolean isNotEmpty(Map<K, V> map) {
        return !isEmpty(map);
    }

    /**
     * 集合元素去重(支持对象属性)
     *
     * @param srcCollection 数据源
     * @param comparator    排序器（根据什么字段或条件排序）
     * @param <T>           泛型
     * @return 返回去重后的数据集合
     * @see com.aitao.test.TestCollector#testDistinct
     */
    public static <T> List<T> distinct(Collection<T> srcCollection, Comparator<T> comparator) {
        return srcCollection.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(comparator)), ArrayList::new));
    }

    /**
     * 集合元素去重
     *
     * @param srcCollection 数据源
     * @param <T>           泛型
     * @return 返回去重后的数据集合
     * @see com.aitao.test.TestCollector#testDistinct
     */
    public static <T> List<T> distinct(Collection<T> srcCollection) {
        //return distinct(srcCollection, null);
        return new ArrayList<>(new HashSet<>(srcCollection));
    }


    /**
     * 求交集
     *
     * @param srcMap  map1
     * @param destMap map2
     * @param <V>     value值类型
     * @return 返回map1与map2中相同元素的集合
     * @see com.aitao.test.TestCollector#testIntersect
     */
    public static <K, V> Map<K, V> intersect(Map<K, V> srcMap, Map<K, V> destMap) {
        Map<K, V> newMap = null;
        if (isNotEmpty(srcMap) && isNotEmpty(destMap)) {
            Set<K> s1 = new HashSet<>(srcMap.keySet());
            s1.retainAll(new HashSet<>(destMap.keySet()));
            newMap = new HashMap<>();
            for (K key : s1) { //构造新Map
                newMap.put(key, srcMap.get(key));
            }
        }
        return newMap;
    }

    /**
     * 求交集
     *
     * @param c1  集合1
     * @param c2  集合2
     * @param <T> 集合数据类型
     * @return 返回c1与c2中相同元素的集合
     * @see com.aitao.test.TestCollector#testIntersect
     */
    public static <T> Collection<T> intersect(Collection<T> c1, Collection<T> c2) {
        Collection<T> retCollection = new HashSet<>(c1);
        retCollection.retainAll(c2);
        return retCollection;
    }

    /**
     * 求并集
     *
     * @param c1  集合1
     * @param c2  集合2
     * @param <T> 集合数据类型
     * @return 返回c1与c2中合并后的结果集
     * @see com.aitao.test.TestCollector#testUnion
     */
    public static <T> Collection<T> union(Collection<T> c1, Collection<T> c2) {
        Collection<T> ls = new LinkedList<>();
        if (isNotEmpty(c1)) {
            ls.addAll(c1);
        }
        if (isNotEmpty(c2)) {
            ls.addAll(c2);
        }
        return ls;
    }

    /**
     * 求并集
     *
     * @param map1 集合1
     * @param map2 集合2
     * @param <K>  键类型
     * @param <V>  值类型
     * @return 返回map1与map2中合并后的结果集
     * @see com.aitao.test.TestCollector#testUnion
     */
    public static <K, V> Map<K, V> union(Map<K, V> map1, Map<K, V> map2) {
        Map<K, V> retMap = new HashMap<>(map1.size() + map2.size());
        if (isNotEmpty(map1)) {
            retMap.putAll(map1);
        }
        if (isNotEmpty(map2)) {
            retMap.putAll(map2);
        }
        return retMap;
    }

    /**
     * 求差集
     *
     * @param c1  集合1
     * @param c2  集合2
     * @param <T> 集合数据类型
     * @return 返回c1与c2中合并后的结果集
     */
    public static <T> Collection<T> subtract(Collection<T> c1, Collection<T> c2) {
        Collection<T> retCollection = new LinkedList<>();
        if (isNotEmpty(c1)) {
            retCollection.addAll(c1);
            if (isNotEmpty(c2)) {
                retCollection.removeAll(c2);
            }
        }
        return retCollection;
    }

    /**
     * 求差集
     *
     * @param map1 集合1
     * @param map2 集合2
     * @param <K>  键类型
     * @param <V>  值类型
     * @return 返回map1与map2
     */
    public static <K, V> Map<K, V> subtract(Map<K, V> map1, Map<K, V> map2) {
        Map<K, V> retMap = new HashMap<>(map1.size() + map2.size());
        if (Validator.isNotEmpty(map1)) {
            Set<K> set1 = new HashSet<>(map1.keySet());
            if (Validator.isNotEmpty(map2)) {
                Set<K> set2 = new HashSet<>(map2.keySet());
                for (K k : set2) {
                    set1.remove(k);
                }
            }
            for (K k : set1) {
                retMap.put(k, map1.get(k));
            }
        }
        return retMap;
    }

    /**
     * 计算最大值
     *
     * @param c   集合
     * @param <T> 类型
     * @return 返回集合元素中的最大值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <T extends Comparable<? super T>> T max(Collection<T> c) {
        return max(c, Comparator.naturalOrder());
    }

    /**
     * 计算最大值
     *
     * @param collection 集合
     * @param comparator 排序器
     * @param <T>        类型
     * @return 返回集合元素中的最大值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <T> T max(Collection<T> collection, Comparator<T> comparator) {
        return ofMin(collection, false, comparator);
    }

    /**
     * 计算最小值
     *
     * @param c   集合
     * @param <T> 类型
     * @return 返回集合元素中的最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <T extends Comparable<? super T>> T min(Collection<T> c) {
        return min(c, Comparator.naturalOrder());
    }

    /**
     * 计算最小值
     *
     * @param collection 集合
     * @param comparator 排序器
     * @param <T>        类型
     * @return 返回集合元素中的最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <T> T min(Collection<T> collection, Comparator<T> comparator) {
        return ofMin(collection, true, comparator);
    }

    /**
     * 计算最大值
     *
     * @param map map容器
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 返回map容器中元素最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <K, V extends Comparable<? super V>> V max(Map<K, V> map) {
        return max(map.values());
    }

    /**
     * 计算最大值
     *
     * @param map        map容器
     * @param comparator 排序器
     * @param <K>        键类型
     * @param <V>        值类型
     * @return 返回map容器中元素最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <K, V> V max(Map<K, V> map, Comparator<V> comparator) {
        return max(map.values(), comparator);
    }

    /**
     * 计算最小值
     *
     * @param map map容器
     * @param <K> 键类型
     * @param <V> 值类型
     * @return 返回map容器中元素最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <K, V extends Comparable<? super V>> V min(Map<K, V> map) {
        return min(map.values());
    }

    /**
     * 计算最小值
     *
     * @param map        map容器
     * @param comparator 排序器
     * @param <K>        键类型
     * @param <V>        值类型
     * @return 返回map容器中元素最小值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    public static <K, V> V min(Map<K, V> map, Comparator<V> comparator) {
        return min(map.values(), comparator);
    }

    /**
     * 计算最大或最小值
     *
     * @param c     集合
     * @param isMin 是否获取最小值
     * @param <T>   类型
     * @return isMin=true时返回最小值,否则返回最大值
     * @see com.aitao.test.TestCollector#testBestValue
     */
    private static <T> T ofMin(Collection<T> c, boolean isMin, Comparator<T> comparator) {
        if (c == null) {
            throw new NullPointerException();
        }
        if (c.isEmpty()) {
            throw new NoSuchElementException();
        }
        return isMin ? c.stream().min(comparator).get() : c.stream().max(comparator).get();
    }

    /**
     * 将Collection容器中的元素值按指定分隔符进行拼接
     *
     * @param collection 集合
     * @param separator  分隔符
     * @param <T>        元素类型
     * @return 将每个元素以指定分隔符连接
     * @see com.aitao.test.TestCollector#testJoin
     */
    public static <T> String join(Collection<T> collection, String separator) {
        StringBuilder retBuilder = new StringBuilder();
        Object[] elements = collection.toArray();
        for (int i = 0; i < elements.length; i++) {
            retBuilder.append(elements[i]);
            if ((i + 1) != elements.length) {
                retBuilder.append(separator);
            }
        }
        return retBuilder.toString();
    }

    /**
     * 将Map容器中的value值按指定分隔符进行拼接
     *
     * @param map       集合
     * @param separator 分隔符
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 拼接后的字符串
     * @see com.aitao.test.TestCollector#testJoin
     */
    public static <K, V> String keyJoin(Map<K, V> map, String separator) {
        return join(map.keySet(), separator);
    }

    /**
     * 将Map容器中的key值按指定分隔符进行拼接
     *
     * @param map       集合
     * @param separator 分隔符
     * @param <K>       键类型
     * @param <V>       值类型
     * @return 拼接后的字符串
     * @see com.aitao.test.TestCollector#testJoin
     */
    public static <K, V> String valueJoin(Map<K, V> map, String separator) {
        return join(map.values(), separator);
    }

    /**
     * 获取子集
     *
     * @param collection 集合
     * @param start      起始位置
     * @param <T>        元素类型
     * @return 返回指定区间的子集
     * @see com.aitao.test.TestCollector#testSubcollect
     */
    public static <T> Collection<T> subcollect(Collection<T> collection, int start) {
        return subcollect(collection, start, collection.size());
    }

    /**
     * 获取子集
     *
     * @param collection 集合
     * @param start      起始位置
     * @param end        结束位置
     * @param <T>        元素类型
     * @return 返回指定区间的子集
     * @see com.aitao.test.TestCollector#testSubcollect
     */
    @SuppressWarnings("unchecked")
    public static <T> Collection<T> subcollect(Collection<T> collection, int start, int end) {
        if (isEmpty(collection)) {
            return new LinkedList<>();
        }
        int subLen = computeIfNotException(start, end, collection.size());
        List<T> retList = new ArrayList<>(subLen);
        Object[] elements = collection.toArray();
        for (int i = start; i < end; i++) {
            retList.add((T) elements[i]);
        }
        return retList;
    }

    /**
     * 检查数组索引的合法性
     *
     * @param startIndex 开始索引
     * @param endIndex   结束索引
     * @param length     数组长度
     * @return 元素个数
     */
    private static int computeIfNotException(int startIndex, int endIndex, int length) {
        Assert.checkArrayIndexOutOfBoundsException(startIndex);
        Assert.checkArrayIndexOutOfBoundsException(endIndex, length);
        Assert.checkArrayIndexOutOfBoundsException(startIndex, endIndex + 1);
        int subLen = endIndex - startIndex;
        Assert.checkArrayIndexOutOfBoundsException(subLen);
        return subLen;
    }
}
